ჰეშირება ჩეინინგით
(Hashing with Chaining).
მარიამ გობრონიძე
Dictionary
Dictionary problem
• insert(item): ახალი მონაცემის ჩამატება
• delete(item): არსებული მონაცემის წაშლა
• search(key): ძებნა
გამოყენებები
შეიძლება ითქვას, რომ dictionary ყველაზე პოპულარული მონაცემთა
სტრუქტურაა კომპიუტერულ მეცნიერებაში, იგი გვხვდება თითქმის
ყველა თანამედროვე პროგრამირების ენაში (Python, Perl, Ruby,
JavaScript, Java, C++, C#, ...)
გამოყენებები
• compilers & interpreters: names → variables
• network routers: IP address → wire
• network server: port number → socket/app.
• virtual memory: virtual address →physical
ჰეშირება
ჰეშირების მეთოდები საშუალებას გვაძლევს შევქმნათ მონაცემთა
სტრუქტურები, რომლებშიც ხორციელდება მხოლოდ სამი
ოპერაცია:
• ელემენტის ჩამატება,
• ელემენტის წაშლა,
• ელემენტის ძებნა
მაგრამ ხორციელდება სხვა სტრუქტურებთან შედარებით
გაცილებით სწრაფად.
იდეა
• მონაცემების (ჩანაწერების) მისამართები ინახება ცხრილში.
• ცხრილში ჩანაწერის ინდექსი გამოითვლება ჩანაწერის გასაღების
მიხედვით.
• ჩანაწერი შეიძლება იყოს სხვადასხვა ტიპის ობიექტი, მაგრამ
გასაღები ყოველთვის არის ნატურალური რიცხვი.
• გასაღებები უნიკალურია, ანუ ორი ჩანაწერის გასაღები არ
შეიძლება ერთმანეთის ტოლი იყოს.
იდეა
გასაღებების მთლიანი რაოდენობა და თვითონ გასაღების სიდიდე
შესაძლოა ბევრად დიდი იყოს, ვიდრე ცხრილის შესაძლო ზომა, რისი
უზრუნველყოფაც შეუძლია სისტემას (ცხრილის ელემენტებზე
მიმართვა უნდა ხდებოდეს ინდექსის საშუალებით, რაც ნიშნავს რომ
ცხრილის დასაპროგრამებლად უნდა გამოვიყენოთ ისეთი
კონტეინერი, რომლის იტერატორს აქვს სწრაფი წვდომა, ანუ
ვექტორი ან უბრალოდ მასივი).
იდეა
იდეალურ შემთხვევაში (კონკრეტულ ამოცანაში გამოსაყენებელი
მეხსიერების მოცულობა შეზღუდული რომ არ იყოს) გასაღებებს
შევინახავდით ამ გასაღების ტოლ მისამართზე (ინდექსით), მაშინ
ნებისმიერი ელემენტის ძებნა განხორციელდებოდა მეხსიერებაზე
ერთადერთი მიმართვით O(1) დროში.
იდეა
რადგან ასეთი იდეალური სიტუაცია პრაქტიკაში არ გვხვდება
(გასაღებები, როგორც წესი ძალიან გრძელია), ამიტომ მივმართავთ
ჰეშირებას და გასაღების მიხედვით ვითვლით ჩანაწერის (ანუ
დინამიკური სიმრავლის ელემენტის) პოზიციას ჰეშ-ცხრილში.
ამ დროს განირჩევა ორი შემთხვევა:
1. როდესაც აქტიური გასაღებების რაოდენობა ყოველ მომენტში
ნაკლებია ცხრილის ზომაზე - ამ დროს ცხრილში ყოველთვის
არსებობს თვისუფალი უჯრები ანუ ღია მისამართები (ღია
მისამართებიანი ჰეშ-ცხრილი, ჩაკეტილი ჰეშირება).
2. როდესაც აქტიური გასაღებების რაოდენობა აღემატება
ცხრილის ზომას (ჯაჭვებით ჰეშირება, ღია ჰეშირება). საშუალედო
შემთხვევები არ არსებობს, ისინი შეგვიძლია გამოვრიცხოთ
ცხრილის ზომების შერჩევის ხარჯზე.
ჰეშ-ფუნქციები
საჭიროა რაიმე ფორმულის საშუალებით O(1) დროში
განვსაზღვროთ ნებისმიერი ჩასამატებელი (ან საძებნი, ან
წასაშლელი) გასაღების მისამართი ცხრილში.
ამისათვის გამოიყენება ჰეშ-ფუნქციები, რომლებიც გასაღებებს
გარდაქმნიან ცხრილის ინდექსებად.
ჰეშ-ფუნქციები
ტრადიციულად, ჰეშ ფუნქციის არგუმენტებს - გასაღებებს , ხოლო
კონკრეტულ გასაღებებზე ჰეშ ფუნქციის მნიშვნელობებს - ჰეშ-
მნიშვნელობებს ვუწოდებთ.
კოლიზიები – დამთხვევები
რადგან გასაღებების მთლიანი რაოდენობა დიდია, ხოლო ცხრილის
უჯრების რაოდენობა შედარებით მცირე, აუცილებლად იჩენს თავს ე.
წ. კოლიზიები, ანუ სხვადასხვა გასაღებისთვის მათი შესაბამისი ჰეშ-
მნიშვნელობის ტოლობა (დამთხვევა).
კოლიზიების გადაჭრა ხდება აქტიური გასაღებების რაოდენობიდან
გამომდინარე:
• ზოგად ჰეშ-ცხრილებში კოლიზიები გადაიჭრება გადაბმის
(სიების, ჯაჭვების) საშუალებით,
• ხოლო ღია მისამართებიან ცხრილებში - მისამართების
გადასინჯვის მეთოდებით.
კოლიზიები – დამთხვევები
პირდაპირი მიმართვის ცხრილი ნიშნავს, რომ შესაძლო
გასაღებთა რაოდენობა მცირეა და გასაღებების სიდიდე არ
აღემატება ცხრილის ზომას, ანუ გასაღებებს წარმოადგენენ
რიცხვები {0, 1, ..., m-1} სიმრავლიდან (m მცირე რიცხვია).
პირდაპირი მიმართვის ცხრილი
პირდაპირი მიმართვის ცხრილი
სიმრავლის შესანახად გამოვიყენოთ m ელემენტიანი T[m]
მასივი, რომელსაც ეწოდება პირდაპირი მიმართვის ცხრილი
(direct-address table). ყოველი პოზიცია, ან უჯრედი (position,
slot) შეესაბამება გარკვეულ გასაღებს U სიმრავლიდან.
ნახ. 1: დინამიკური სიმრავლის რეალიზაცია პირდაპირი მიმართვის T
ცხრილის საშუალებით: U ={0,1,…,9} წარმოადგენს ყველა შესაძლო
გასაღების სიმრავლეს. თითოეულ გასაღებს ამ სიმრავლიდან T
ცხრილში შეესაბამება საკუთარი უჯრედი. ცხრილის 2, 3, 5 და 8
ნომრის შესაბამის პოზიციებში (ფაქტობრივად გამოყენებული
გასაღებები) ჩაწერილია მიმთითებლები სიმრავლის ელემენტებზე,
ხოლო ცხრილის გამოუყენებელ პოზიციებში (მუქი ფერითაა
მოცემული) ჩაწერილია NULL.
აღვნიშნოთ T[k]- თი ცხრილის ის პოზიცია, რომელშიც იწერება k
გასაღების მქონე ელემენტის (ჩანაწერის) x მისამართი. თუ k
გასაღების მქონე ელემენტი ცხრილში არა გვაქვს, მაშინ T[k]= NULL.
ლექსიკონური ოპერაციების რეალიზება ასე მოხდება:
თითოეული ამ ოპერაციიდან საჭიროებს O(1) დროს. პრაქტიკაში,
პირდაპირი მიმართვის ცხრილი იშვიათად გამოიყენება.
ჰეშ-ფუნქციები
ჰეშ-ფუნქციის დანიშნულება არის, რომ გასაღებები (უნიკალური
ნატურალური რიცხვები, ან მთლიანი ობიექტები) გარდაქმნას
ერთგანზომილებიანი ცხრილის ინდექსებად, ანუ მისამართებად.
ცხრილს რა კონტეინერით დავაპროგრამებთ ამ მომენტში
მნიშვნელობა არ აქვს. ცხრილის დანომრვა იწყება ნულიდან. მისი
ზომა აღვნიშნოთ m-ით, თვითონ ცხრილი T-თი.
ჰეშ-ფუნქციები
განვიხილოთ ჰეშ-ფუნქციის აგების ორი მეთოდი:
1. ნაშთიანი გაყოფა
2. გამრავლება
შენიშვნა
პრაქტიკაში მათი გამოყენება დამოკიდებულია ამოცანის
სპეციფიკაზე. ზოგადად, კარგი ჰეშ ფუნქციისაგან მოითხოვება, რომ
მან უზრუნველყოს თანაბარი ჰეშირება. ჩვეულებისამებრ,
გულისხმობენ რომ ჰეშ-ფუნქციების განსაზღვრის არეში
ნატურალური რიცხვებია. თუკი გასაღებები არ წარმოადგენენ
ნატურალურ რიცხვებს, მათ გარდაქმნიან ასეთ სახემდე.
გაყოფის მეთოდი
მდგომარეობს იმაში, რომ ჰეშ-ფუნქცია k სიდიდის მქონე გასაღებს
შეუსაბამებს k-ს m- ზე გაყოფის ნაშთს:
h(k)= k mod m, ანუ h(k)= k%m.
მაგალითად, თუკი ჰეშ-ცხრილის ზომა m = 12 და გასაღები უდრის
100-ს, მაშინ შესაბამისი ჰეშ მნიშვნელობა იქნება 4.
გაყოფის მეთოდი
m-ის გარკვეულ მნიშვნელობებს თავი უნდა ავარიდოთ. მაგალითად
თუ m = 2^p , მაშინ h(k) წარმოადგენს k რიცხვის p უმცროს ბიტს.
თუკი დარწმუნებული არა ვართ, რომ გასაღებთა უმცროსი ბიტები
ერთნაირი სიხშირით შეგვხვდება, m რიცხვის როლში ორის
ხარისხები არ უნდა ავიღოთ. არასასურველია m -ის მნიშვნელობად
10-ის ხარისხების არჩევა თუკი გასაღებები ათობით რიცხვებს
წარმოადგენენ – ასეთ შემთხვევაში აღმოჩნდება, რომ გასაღებთა
ციფრების ნაწილი მთლიანად განსაზღვრავს ჰეშ-მნიშვნელობებს.
კარგ შედეგებს იძლევა m -ის როლში ისეთი მარტივი რიცხვის აღება,
რომელიც შორს დგას 2-ის ხარისხებისაგან. მაგალითად, თუ ჰეშ-
ცხრილში შესატანია 2000-მდე ჩანაწერი და ჯაჭვში ანუ სიაში
დაახლოებით სამი ვარიანტის გადარჩევა პრობლემას არ ჰქმნის
ელემენტთა ძებნისას, m ის მნიშვნელობად შეგვიძლია ავიღოთ 701.
რიცხვი 701 მარტივია, 701 ≈ 2000/3 და ორის ხარისხებისგანაც შორს
დგას. ამის გამო მიზანშეწონილია ავირჩიოთ ჰეშ-ფუნქცია h(k) = k
mod 701.
გამრავლების მეთოდი
მდგომარეობს შემდეგში – ვთქვათ ჰეშ მნიშვნელობების რაოდენობა
m -ის ტოლია. დავაფიქსიროთ რაიმე A მუდმივი 0 < A < 1 და
განვსაზღვროთ:
h(k)= m 🇽 ((kA)mod1)
სადაც (kA)mod1 წარმოადგენს kA -ს წილად ნაწილს.
გამრავლების მეთოდი
გამრავლების მეთოდის ღირსება იმაში მდგომარეობს, რომ ჰეშ-
ფუნქციის ეფექტურობა ნაკლებადაა დამოკიდებული m -ის
არჩევაზე. ჩვეულებისამებრ m -ის როლში ირჩევენ ორის ხარისხს,
რადგან კომპიუტერთა უმეტესობაში ასეთ m -ზე გამრავლება
რეალიზდება როგორც სიტყვის წანაცვლება.
გამრავლების მეთოდი
გამრავლების მეთოდი მუშაობს A მუდმივის ნებისმიერი
მნიშვნელობისათვის, მაგრამ ზოგიერთმა მნიშვნელობამ შეიძლება
უკეთესი შედეგი მოგვცეს. ითვლება, რომ
A = (√5-1) /2 = 0.6180339887
მნიშვნელობა საკმაოდ ეფექტურია.
გამრავლების მეთოდი
მაგალითად: k=123456, m=10000 და A განსაზღვრულია (1)
ფორმულით. მაშინ:
h(k)= m 🇽 ((kA)mod1)
k გასაღების მქონე ელემენტი ჩაიწერება h(k) ნომრის მქონე
პოზიციაზე T[m] ჰეშ-ცხრილში (hash table), სადაც
h : U → {0, 1, 2, … m-1}
რომელიმე ჰეშ-ფუნქციაა. h(k) k გასაღების ჰეშ-მნიშვნელობა (hash
value). ჰეშირების იდეა ნაჩვენებია ნახ. 2-ზე, სადაც ვიყენებთ არა U,
არამედ m სიგრძის მასივს და ამით ვზოგავთ მეხსიერებას.
ჰეშირება სიებით, ანუ ჯაჭვებით
კოლიზია – დამთხვევა
კოლიზია – ორი ან მეტი გასაღების ჰეშ-მნიშვნელობა შეიძლება
დაემთხვეს. როდესაც U>m, იარსებებენ განსხვავებული გასაღებები,
რომელთაც ერთნაირი ჰეშ-მნიშვნელობები ექნებათ. ჰეშ-ფუნქციის
შერჩევისას ყოველთვის არ ვიცით, თუ რომელი გასაღებები იქნება
შენახული. ჰეშ-ფუნქცია რაღაც აზრით შემთხვევითი უნდა იყოს,
რათა კარგად “გააბნიოს” გასაღებები უჯრედებში. თუმცა, ასეთი
შემთხვევითი ჰეშ-ფუნქციაა იმდენად მაინც უნდა იყოს
დეტერმინირებული, რომ განმეორებითი გამოძახებების დროსაც
ერთ და იგივე არგუმენტისათვის ერთნაირი ჰეშ-მნიშვნელობა
მიიღოს.
ჰეშიება გადაბმით – ჯაჭვებით
ახლა გავარჩიოთ კოლიზიის გადაჭრა ბმული სიების გამოყენებით.
ჰეშიება გადაბმით – ჯაჭვებით
არსი – ერთნაირი ჰეშ-მნიშვნელობის მქონე ელემენტებისაგან
ყალიბდება ბმული სია. j -ურ პოზიციაში ინახება მიმთითებელი იმ
ელემენტთა სიაზე, რომელთა გასაღებების ჰეშ-მნიშვნელობა j-ს
ტოლია. თუკი ასეთი ელემენტები არ არსებობენ, j-ურ პოზიციაში
იწერება NULL.
ჰეშიება გადაბმით – ჯაჭვებით
დამატების, ძებნისა და წაშლის ოპერაციები იოლად რეალიზდება:
ჰეშიება გადაბმით – ჯაჭვებით
დამატების ოპერაცია უარეს შემთხვევაში მუშაობს O(1) დროში.
ძებნის ოპერაციის მუშაობის დრო სიის სიგრძის პროპორციულია,
თუმცა კარგად შერჩეული პარამეტრების შემთხვევაში მისი
სისწრაფე, ისევე როგორც ელემენტის წაშლისა ორმხრივ ბმულ
სიაში, შეიძლება განხორციელდეს O(1) დროში. თუკი სიები
ცალმხრივადაა ბმული, მაშინ x ელემენტის წასაშლელად საჭიროა
წინასწარ ვიპოვოთ მის წინ მდგომი ელემენტი. ზოგადად, საკმაოდ
გავრცელებულია მიდგომა, რომ სიაში შევინახოთ არა თვითონ
ობიექტები, არამედ მათი მისამართები.
სიტუაციები, სადაც ჰეში არ გამოიყენება
მონაცემების დალაგებული ფორმით შენახვა ძიება , ჩასმა და წაშლა – ასეთ
შემთხვევებში ვიყენებთ თვითბალანსირებად ბინარულ ძებნის ხეს (Self-Balancing
BST).
როდესაც გასაღებები სტრიქონებია და საჭიროა პრეფიქსული ძიება ძიებასთან ,
ჩასმასთან და წაშლასთან ერთად – ასეთ შემთხვევებში ვიყენებთ ტრაის (Trie).
როდესაც საჭიროა ისეთი ოპერაციები , როგორიცაა მინიმალური და მაქსიმალური
ზღვარის პოვნა (floor და ceiling) ძიებასთან , ჩასმასთან და/ან წაშლასთან ერთად
– ასეთ შემთხვევებში ვიყენებთ თვითბალანსირებად ბინარულ ხეებს.
როგორ მუშაობს ჰეშირება?
მაგალითი
დავუშვათ, რომ გვაქვს სტრიქონების ნაკრები {“ab”, “cd”, “efg”} და
გვინდა მათი შენახვა ცხრილში.
1. ჰეშირების პრინციპი:
ჰეშირებისას გამოიყენება ჰეშ-ფუნქცია (განსაზღვრული მათემატიკური
ფორმულა), რომელიც ითვლის ჰეშ-მნიშვნელობას . ეს მნიშვნელობა
წარმოადგენს ინდექსს , სადაც მონაცემი შეინახება მონაცემთა
სტრუქტურაში.
2. სიმბოლოების რიცხვითი
მნიშვნელობების მინიჭება:
ვანიჭებთ თითოეულ ანბანურ სიმბოლოს რიცხვით მნიშვნელობას:
● “a” = 1,
● “b” = 2,
● “c” = 3, ... და ასე შემდეგ.
3. სტრიქონების რიცხვითი მნიშვნელობების
გამოთვლა:
ჰეშ-ფუნქციის მარტივი ვარიანტია სტრიქონის სიმბოლოების ჯამის გამოთვლა :
● “ab” = 1 + 2 = 3
● “cd” = 3 + 4 = 7
● “efg” = 5 + 6 + 7 = 18
მოდით, წარმოვიდგინოთ, რომ გვაქვს ცხრილი ზომით 7.
ჰეშ-ფუნქცია, რომელსაც ვიყენებთ, არის:
სტრიქონების შენახვის მისამართი განისაზღვრება ჯამის მოდულო 7-ით (mod 7):
● “ab” → 3 mod 7 = 3 → ინახება ინდექსზე 3
● “cd” → 7 mod 7 = 0 → ინახება ინდექსზე 0
● “efg” → 18 mod 7 = 4 → ინახება ინდექსზე 4
4. ჰეშ-ცხრილის შექმნა და ჰეშ-ფუნქციის
გამოყენება:
ჩეინინგით ჰეშირება (Chain Hashing) და
კოლიზიის თავიდან აცილება
ჰეშირების პროცესში ჰეშ-ფუნქცია (Hash Function) გასაღებებს (keys) გარკვეულ
მნიშვნელობებზე ასახავს (maps). თუმცა, ამ ფუნქციამ შესაძლოა გამოიწვიოს კოლიზია
(Collision), როდესაც ორი ან მეტი გასაღები ერთსა და იმავე ინდექსზე ხვდება.
ჩეინინგით ჰეშირება (Chain Hashing) ეფექტურად უმკლავდება ამ პრობლემას. იდეა
მდგომარეობს იმაში, რომ ჰეშ-ცხრილის თითოეული უჯრა (bucket) მიუთითებს ბმულ სიაზე
(Linked List), რომელიც შეიცავს იმავე ჰეშ-ფუნქციის მნიშვნელობის მქონე ჩანაწერებს .
ჰეშ-ფუნქციის შექმნა
მოდით, განვსაზღვროთ ჰეშ-ფუნქცია, სადაც ჰეშ-ცხრილს აქვს N უჯრა(bucket).
გასაღების ინდექსის გამოთვლა :
hashIndex=key mod numberOfBuckets
ელემენტის ჩასმა (Insert) ჰეშ-ცხრილში
1. გამოვთვლით ჰეშ-ინდექსს მოცემული გასაღებისთვის.
2. ვიპოვით შესაბამის უჯრა გამოთვლილი ჰეშ-ინდექსის მიხედვით.
3. ახალ კვანძს (Node) ვამატებთ ამ ბაკეტის ბმულ სიაში (Linked List).
ელემენტის წაშლა (Delete) ჰეშ-ცხრილიდან
1. გამოვთვლით გასაღებისთვის ჰეშ-ინდექსს .
2. გადავინაცვლებთ შესაბამის ბაკეტში (bucket).
3. ვეძებთ ბმულ სიაში (Linked List) გასაღების შესაბამის კვანძს.
4. თუ ვიპოვით, ვშლით მას.
ეს ნიშნავს, რომ ჰეშ-ცხრილს ექნება 7 bucket-ი (სექცია ან კონტეინერი ), სადაც მონაცემები
შეინახება.
2. ჰეშის კლასის შექმნა
__init__ მეთოდი :
● იღებს bucket-ის ზომას და
ქმნის ჰეშ-ცხრილს, რომელიც
არის (list) სია:
● self.__table = [[]
for _ in
range(bucket)] →
თითოეულ ინდექსზე არის
ცარიელი სია ([]), რომელიც
შეინახავს ელემენტებს.
3. ჰეშ-ფუნქცია
ეს მეთოდი ჰეშ-ფუნქციის მეშვეობით გასაღების (key) bucket-ის ინდექსს
ადგენს.
მათემატიკურად — key % bucket_size.
მაგალითად, თუ key = 15 და bucket_size = 7:
15mod 7=1
ეს ნიშნავს, რომ 15 უნდა შევინახოთ ინდექს 1-ზე.
4. ელემენტის ჩასმა (insert) ჰეშ-ცხრილში
ჰეშ-ფუნქციით ვპოულობთ ინდექსს , სადაც ელემენტი უნდა
შევინახოთ.
ვამატებთ მას შესაბამის bucket-ში (append მეთოდით).
5. ელემენტის წაშლა (delete)
ვპოულობთ წასაშლელ ინდექსს.
ვამოწმებთ, არსებობს თუ არა
ელემენტი ამ bucket-ში.
თუ არსებობს, ვშლით მას
remove() მეთოდით.
6. ჰეშ-ცხრილის ჩვენება (Display)
12-ის წაშლის შემდეგ
დროითი სირთულე
● ძებნა : O(1+(n/m))
● წაშლა : O(1+(n/m))
სადაც n = ჰეშ ცხრილში ელემენტების საერთო რაოდენობა
m = ჰეშ ცხრილის ზომა
Load Factor (ტვირთვის კოეფიციენტი ) ჰეშ-ცხრილის ეფექტურობის გასაზომად გამოიყენება.
იგი განისაზღვრება როგორც:
Load Factor=n/m
სადაც:
● n – ჰეშ-ცხრილში შენახული ელემენტების რაოდენობა (keys count)
● m – ჰეშ-ცხრილის ბაკეტების რაოდენობა (buckets count)
Load Factor (ტვირთვის კოეფიციენტი)
ჰეშირებაში
Load Factor-ის მნიშვნელობა და გავლენა
თუ Load Factor ძალიან მცირეა (< 0.5) → ჰეშ-ცხრილში ბევრი ცარიელი ბაკეტია,
მეხსიერების ფუჭი ხარჯვა ხდება.
თუ Load Factor ძალიან დიდია (> 1.0) → ბევრი ელემენტი ერთსა და იმავე
ბაკეტში ხვდება, კოლიზიები იზრდება და ძიების სიჩქარე მცირდება.
იდეალური Load Factor ჩვეულებრივ 0.7-ის ფარგლებშია , რაც ბალანსს ქმნის
მეხსიერებასა და ძიების სიჩქარეს შორის.
როდესაც Load Factor იზრდება – Rehashing
(გადახარისხება)
თუ Load Factor ზედმეტად დიდი ხდება , შეიძლება Rehashing მოვახდინოთ:
1. ვქმნით ახალ, უფრო დიდ ჰეშ-ცხრილს (მაგ., ორჯერ უფრო მეტ ბაკეტს ვამატებთ).
2. ძველი ცხრილიდან ყველა გასაღებს თავიდან ვინახავთ ახალ ცხრილში.
3. ეს ამცირებს კოლიზიებს და ზრდის ჰეშ-ცხრილის ეფექტურობას.
დავუშვათ, გვაქვს ჰეშ-ცხრილი m = 5 ბაკეტით და მასში 10 გასაღებია:
Load Factor=10/5=2
ეს ნიშნავს, რომ ერთ ბაკეტში საშუალოდ 2 ელემენტია , რაც კოლიზიებს ზრდის.
თუ ბაკეტების რაოდენობას გავაორმაგებთ (rehashing), ვიღებთ:
Load Factor=10/10=1
კოლიზიები მცირდება, ძიება სწრაფდება.
👉 Load Factor-ის კონტროლი აუცილებელია ჰეშ-ცხრილის ეფექტურად მუშაობისთვის !
მაგალითი
გმადლობთ ყურადღებისთვის!
